package de.szut.tree;

import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.function.UnaryOperator;

/**
 * Linear directions for binary search tree node iteration
 * @author Marc Huisinga
 *
 */
public enum IterationDirection {
	
	/**
	 * Iterates into the lower left direction of a tree.
	 */
	LEFT {
		@Override
		<K, V> Generator<Node<K, V>> iterate(Node<K, V> start) {
			return linearDirection(start, node -> node.getLeftChild());
		}
	},
	
	/**
	 * Iterates into the lower right direction of a tree.
	 */
	RIGHT {
		@Override
		<K, V> Generator<Node<K, V>> iterate(Node<K, V> start) {
			return linearDirection(start, node -> node.getRightChild());
		}
	},
	
	/**
	 * Iterates a tree upwards.
	 */
	UPWARDS {
		@Override
		<K, V> Generator<Node<K, V>> iterate(Node<K, V> start) {
			return linearDirection(start, node -> node.getParent());
		}
	};
	
	/**
	 * Iterates a tree until an end is reached from the inclusive start node.
	 * @param start - node to start iterating at
	 * @return generator for lazy evaluation
	 */
	abstract <K, V> Generator<Node<K, V>> iterate(Node<K, V> start);
	
	/**
	 * Iterates a tree until an end is reached from the inclusive start node 
	 * into the direction provided in the nextGetter.
	 * @param start - node to start iterating at
	 * @param nextGetter - getter for the next node to iterate over
	 * @return generator for lazy evaluation
	 */
	private static <K, V> Generator<Node<K, V>> linearDirection(Node<K, V> start, UnaryOperator<Node<K, V>> nextGetter) {
		return () -> {
			return new Iterator<Node<K, V>>() {

				Node<K, V> currentNode = start;
				
				@Override
				public boolean hasNext() {
					return currentNode != null;
				}

				@Override
				public Node<K, V> next() {
					if (!hasNext()) {
						throw new NoSuchElementException("End has been reached.");
					}
					Node<K, V> previousNode = currentNode;
					currentNode = nextGetter.apply(currentNode);
					return previousNode;
				}
				
			};
		};
	}
	
}
